<?php
namespace Common\Model;
use Think\Model;
use Home\Service\ClockTimeService;
use Home\Service\AccountService;
// +----------------------------------------------------------------------
// | Date:2017年2月9日
// +----------------------------------------------------------------------
// | Author: EK_熊<1439527494@qq.com>
// +----------------------------------------------------------------------
// | Description: 考勤数据当天汇总表
// +----------------------------------------------------------------------
class AttendanceDayModel extends Model{
    protected $rebulid_att_id=[];//数据重建时，已经执行的申请id存放地
    
    /**
     * 汇总当天员数据,使用指纹码(uid有可能会滞后添加导致uid=0)
     * 
     * date:2017年2月9日
     * author: EK_熊
     */
    public function collect_day($attendance,$auto = true){
        $attenModel = D('Attendance');
        $optionModel = D('Options');
        $applyModel = D('Apply');
        $workday = C('ACTUAL_WORK_DAY');
        $day_start_time = $workday[0];
        if ($auto) {
            /*修改配置，更新最新的自增长ID*/
            $last_element = end($attendance);//数据集合的最大dev_id
            $devname = get_server_devcode($last_element['dev_code']);
            $cfg_name = "att_cfg_$devname";
            $preDevId = $optionModel->getOption($cfg_name);
            $optionModel->setOption($cfg_name,["last_id"=>$last_element['dev_id']]);
        }
        foreach ($attendance as $k=>$v){
            if ($v['sn'] == '') continue;//没有工号，就跳过不理会
            /*1.先把考勤流水记录入库*/
            $addAttendce = $attenModel->write_to_db($v);
            
            if (!$v || !$v['sn']) continue;
            $curLog = "[{$v['dev_id']}][{$v['name']}]";
            
            if ($addAttendce === false) {
                $this->error = "流水记录数据创建错误！";
                break;
            }
            $curClockTime = strtotime($v['time']);
            
            /* 检查汇总记录是否存在员工当天数据 */
            $attenDayInfo = $this->isExist($v['sn'], $curClockTime);

            /* 用户信息同步更新 */
            $this->updateUserInfo($attenDayInfo, $v);
            
            //获取审核通过的申请数据
            $app_time_list = $applyModel->get_time_list_att($v['sn'],$v['time']);
            
            $clocktimeService = new ClockTimeService($attenDayInfo['duty_on'],$curClockTime);//实例化考勤类
            $attInfo = $clocktimeService->attendance_info($app_time_list);

            //新增操作
            $ret = true;
            if (empty($attenDayInfo)) {
                //新增操作
                $add = [
                    'date'=>$clocktimeService->get_date(),
                    'date_str'=>get_week($curClockTime),
                    'duty_on'=>$v['time'],
                    'duty_off'=>$v['time'],
                    'clock_num'=>1,//打卡次数
                    'creaetd_time'=>CURRENT_TIME,
                ];
                
                $add = array_merge($add,$attInfo,$v);
                $ret = $this->add($add);
                //出错，跳出循环
                if (!$ret) {
                   $this->error = "每日考勤数据创建错误！";
                   break;
                }
            }else{
                if (strtotime($attenDayInfo['duty_off']) >= strtotime($v['time'])) {
                    continue;//打卡时间必须比之前的数据大，才可以更新
                } 
                
                $save = $attInfo;
                
                //获取全部假期数
                $save['clock_num'] =$attenDayInfo['clock_num'] + 1;//打卡次数
                $save['updated_time'] =CURRENT_TIME;//打卡次数
                $save['duty_off'] = $v['time'];
                if (!isset($attenDayInfo['duty_on'])) $save['duty_on'] = $v['time'];
                
                $where['sn'] = $v['sn'];
                $where['date'] = $clocktimeService->get_date();
   
                $ret = $this->where($where)->save($save);
                //出错，跳出循环
                if (!$ret) {
                    $this->error = "每日考勤数据更新错误！";
                    break;
                }

            }

        }

        
        if ($this->error) {
            $this->error = $curLog.$this->error;
            $optionModel->setOption($cfg_name,["last_id"=>$preDevId['dev_id'],'limit'=>0]);//报错了，停止拉取数据
            return false;
        }
        return true;

    }
    
    
    /**
     * 打卡点的过滤检查，主要检查和申请数据的关系
     * 打卡的时候，检查是否那天有申请数据，是否对打卡时间产生影响
     * @param unknown $day
     * @param unknown $sn
     * 返回  两个打卡点，返回false，阻止更新打卡，
     * date:2017年6月9日
     * author: EK_熊
     */
    public function duty_on_off($duty_on,$clocktime,$sn){
        $clockService = new ClockTimeService($clocktime);
        $clockDay = $clockService->get_date();//获取打卡的日期
        $applyModel = M('Apply');
        $approverModel = D('Approver');
        if (!$duty_on) $duty_on = $clocktime;
        
        if($duty_on && ($duty_on > $clocktime)) {
            $a = $clocktime;
            $clocktime = $duty_on;
            $duty_on = $a;
        }
        
        $ret['duty_on'] = $duty_on;
        $ret['duty_off'] = $clocktime;
        
        //通过日期查找申请数据
        $map_apply['sn'] = $sn;
        $map_apply["DATE_FORMAT(start_time,'%Y-%m-%d')"] = ['elt',$clockDay];
        $map_apply["DATE_FORMAT(end_time,'%Y-%m-%d')"] = ['egt',$clockDay];
        $map_apply['status'] = 1;//必须是通过的申请，时间条件会过滤掉补打卡的情况
        $applyInfo = $applyModel->where($map_apply)->find();
        if (!$applyInfo) return $ret;//没有申请数据，当然简单返回
        if ($applyInfo['type'] == 1) {
            //type是1，外出情况，才需要处理
            $applyDate = $approverModel->get_time_start_end($applyInfo,$clockDay);//过滤一下，获取申请数据的起止时间，有可能是多天假期
            $applyInfo['start_time'] = $applyDate['on'];
            $applyInfo['end_time'] = $applyDate['off'];
    
            //短途出差，1天而已
            if ($duty_on == $clocktime && $clocktime >= $applyInfo['start_time'] && $clocktime <= $applyInfo['end_time']) {
                return false;
            }
            
            if ($duty_on <= $applyInfo['start_time'] && $clocktime >= $applyInfo['end_time']) {
                
            }elseif(($duty_on >= $applyInfo['end_time'])||($duty_on <= $applyInfo['end_time'] && $duty_on>=$applyInfo['start_time'] && $clocktime >= $applyInfo['end_time'])){
                
                $ret['duty_on'] = $applyInfo['start_time'];
            }elseif($duty_on < $applyInfo['end_time'] && $clocktime <=$applyInfo['end_time'] && $duty_on >= $applyInfo['start_time']){
                $ret['duty_on'] = $applyInfo['start_time'];
                $ret['duty_off'] = $applyInfo['end_time'];
            }elseif(($clocktime <= $applyInfo['start_time'])||($duty_on <= $applyInfo['start_time'] && $duty_on < $applyInfo['end_time'] && $clocktime <= $applyInfo['end_time'])){
                $ret['duty_off'] = $applyInfo['end_time'];
            }
        }
        return $ret;

    }
    
    
    /**
     * 同步更新用户信息
     * @param unknown $oldInfo
     * @param unknown $newInfo
     * @param unknown $key    '关联字段'
     * date:2017年2月16日
     * author: EK_熊
     */
    public function updateUserInfo($oldInfo,$newInfo,$key='sn'){
        if (!$newInfo) return null;
        
        if ($oldInfo['name'] !== $newInfo['name'] || $oldInfo['dep_name'] !== $newInfo['dep_name'] || $oldInfo['uid'] !== $newInfo['uid']) {
            $updateUser['name'] = $newInfo['name'];
            $updateUser['uid'] = $newInfo['uid'];
            $updateUser['dep_name'] = $newInfo['dep_name'];
            $updateUser['dep_id'] = $newInfo['dep_id'];
        }
        if ($updateUser) {
            $where[$key] = $newInfo[$key];
            $ret = $this->where($where)->save($updateUser);
        }
        return $ret;
    }
    
    
    /**
     * 检查当天是否有数据存在
     * @param unknown $sn
     * @param unknown $clockTime
     * date:2017年2月21日
     * author: EK_熊
     */
    public function isExist($sn,$clockTime){
        $clocktimeService = new ClockTimeService($clockTime);
        $where['sn'] = $sn;
        $where['date'] = $clocktimeService->get_date();
        $ret = $this->where($where)->find();
        return $ret;
    }
    
    
    /**
     * 计算时间范围内的累计可补休的加班时间
     * @param unknown $sn
     * @param unknown $start
     * @param unknown $end
     * date:2017年3月1日
     * author: EK_熊
     */
    public function count_remain_overtime($sn,$start='',$end){
        if (empty($start)) $start = date("Y-m-d",strtotime("$end -60 day"));
        $map['_string'] = "`overtime` > `leave_compensatory`";
        $map['sn'] =$sn;
        $map['date']=['between',[$start,$end]];
        $overtime = $this->where($map)->field("(sum(overtime)-sum(compensatory_consume)) as remain_compensatory")->select();
        
        //要扣除待审核的可补休时间总值
        $overtime_apply_wait = D('Apply')->min_wait_in_apply($sn, 101,$start, $end);
        $compensatory = intval($overtime[0]['remain_compensatory']) - $overtime_apply_wait;
        return $compensatory < 0 ? 0 : $compensatory ;
    }
    
    
    
    
    
    /**
     * 计算时间范围内的,该部门的全部人的累计可补休的加班时间,返回键值对，[工号=>可补休时间]
     * @param unknown $start
     * @param unknown $end
     * date:2017年6月6日
     * author: EK_熊
     */
    public function count_remain_overtime_all($dep_id='',$end){
        
        $start = date("Y-m-d",strtotime("$end -60 day"));//60天以前
      //  $end = date('Y-m-d', strtotime("-1 day"));//昨天
        $map['_string'] = "`overtime` > `leave_compensatory`";
        $map['date']=['between',[$start,$end]];
        if ($dep_id) $map['dep_id'] = $dep_id;
        $overtime = $this->where($map)->group('sn')->getField("sn,(sum(overtime)-sum(compensatory_consume)) as remain_compensatory");
        //获取该部门下面的，待审核状态的可补休记录
        $map_aply['vacate_type'] =101;
        $map_aply['status'] =0;
        $map_aply['dep_id'] =$dep_id;
        $map_aply['start_time']=['egt',"$start 00:00:00"];
        $map_aply['end_time']=['elt',"$end 23:59:59"];
        
        $overtime_aply = M('Apply')->where($map_aply)->group('sn')->getField("sn,sum(total_min) as total_min");
        if ($overtime_aply) {
            foreach ($overtime_aply as $k=>$v) {
                $compensatory = $overtime[$k] - $v;
                $overtime[$k] = $compensatory > 0 ? $compensatory : 0;
            }
        }
        return $overtime;        
    }
    
    /**
     * 
     * 计算时间范围内的累计缺勤时间
     * date:2017年3月1日
     * author: EK_熊
     */
    public function count_absent($sn,$start,$end){
        $map['sn']=$sn;
        $map['date']=['between',["$start 00:00:00","$end 23:59:59"]];
        $absent = $this->where($map)->sum('stu_absent');
        return $absent;        
    }
    
    
    /**
     * 销毁申请数据
     * 1.修改目标数据状态，2删除日常考勤数据，进行重建，3通过那个时间段的全部申请数据
     *
     * date:2017年5月15日
     * author: EK_熊
     */
    public function att_day_rebuild($day='',$name=''){
        
        $applyModel = D('Apply');
        $approverModel =D('Approver');
        //删除旧的日常考勤数据
        $map_day['date'] = $day;
        $map_day['name'] = $name;
        $del = $this->where($map_day)->delete();
        
         //日常考勤数据重建
        $reload = $this->att_day_reload($day,$name);
        if(!$reload) {
            $this->error = $this->getError();
            return false;
        }
        
        //处理申请数据
        $map_apply['_string']="(`type`=4 AND DATE_FORMAT(end_time,'%Y-%m-%d')='$day') OR (DATE_FORMAT(start_time,'%Y-%m-%d') = '$day') OR (DATE_FORMAT(start_time,'%Y-%m-%d') <= '$day' AND DATE_FORMAT(end_time,'%Y-%m-%d') >= '$day')";
        $map_apply['status'] = 1;//必须是审核通过的数据才需要重新
        if ($name) $map_apply['name'] = $name;
        
        $ids = $applyModel->where($map_apply)->group('CONCAT(start_time,end_time)')->getField('id',true);//使用group+concat组合去除重复记录

        if ($ids) {
            foreach ($ids as $appid){
                if (!in_array($appid,$this->rebulid_att_id) || empty($this->rebulid_att_id)){
                    $ret = $approverModel->_action_pass($appid);
                    if ($ret) {
                        $this->rebulid_att_id[] = $appid;
                    }else{
                        $err_id[] = $appid;
                    }
                    
                }
            }
        }
            
        if ($err_id){
            $this->error = '申请id['.implode(',',$err_id).']处理出错！';
            return false;
        }
        return true;
    }
    
    
    
    
    
    /**
     * 清除旧数据，重载用户个人新的考勤数据
     * 1.根据条件（姓名和日期，先删除旧数据，再重新拉取流水数据再次生成日常数据）
     * 2.删除用户相关的 申请记录，审核人记录
     * @param unknown $day   日期
     * @param unknown $name
     * date:2017年3月13日
     * author: EK_熊
     */
    public function att_day_reload($day,$name=''){
        $optionModel = D('Options');
        if ($name) $map['name'] = trim($name);
        //计算一个工作日的时间范围
        $rang_date = get_actual_day_rang($day);
        $date_s = $rang_date[0];
        $date_e = $rang_date[1];
        $map['recept_time'] = ['between',[$date_s,$date_e]];
        $attenData = M('Attendance')->where($map)->field('dev_id,name,sn,recept_time as time')->order('recept_time')->select();
        $ret = true;
        if ($attenData){
            /*修改配置，关闭自动拉取功能*/
            $optionModel = D('Options');
            $optionModel->setOption('dev_loader',['limit'=>0]);

            //从员工数据平台获取员工信息
            $attenData= D('Home/Account','Service')->append_user_field('sn',$attenData,'sn,ep_name,dep_id,dep_name,ep_id as uid');
            if (!$attenData) {
                $this->error = '用户信息获取失败，可能没有员工编号';
                return false;
            }
            
            $ret = $this->collect_day($attenData,false);
            
            $optionModel->setOption('dev_loader',['limit'=>50]);//重启自动拉取
        }
        return $ret;
    }
    
    
    
    /**
     * 设置写入用户的调休时间的消耗数
     * @param unknown $sn
     * @param unknown $min
     * date:2017年3月21日
     * author: EK_熊
     */
    public function set_compensatory_consume($appInfo){
        
        $sn = $appInfo['sn'];
        $min = $appInfo['total_min'];
//         $curdate = date('Y-m-d',strtotime($appInfo['start_time']));
        $publictime = $appInfo['created_time'];
        
        $start_from_60 = date("Y-m-d",strtotime("$publictime -60 day"));//记录发布时间的60天以前
        $lastday_end = date('Y-m-d', strtotime("$publictime -1 day"));
        
        $cur_start_from_60 = date("Y-m-d",strtotime("-60 day"));//60天以前
        $cur_lastday_end = date('Y-m-d', strtotime("-1 day"));
        
        $map['_string'] = "(`overtime` > `compensatory_consume`) AND ((`date` BETWEEN '$start_from_60 00:00:00' AND '$lastday_end 23:59:59') OR (`date` BETWEEN '$cur_start_from_60 00:00:00' AND '$cur_lastday_end 23:59:59')) ";
        $map['sn'] =$sn;
    //    $map['date'] = ['between',["$start_from_60 00:00:00","$lastday_end 23:59:59"]];
        $att = $this->where($map)->order('date asc')->find();

        if (!$att) {
            $this->error = '没有可调休的时间，无须操作~';
            return false; 
        }
        $where = ['sn'=>$sn,'date'=>$att['date']];

        if ($att['overtime'] >= $min){
            if ($att['compensatory_consume'] == 0) {
                //直接写入
                $update = $this->where($where)->setField('compensatory_consume',$min);
            }else{
                
                if (($att['compensatory_consume'] + $min) > $att['overtime']){
                    $update = $this->where($where)->setField('compensatory_consume',$att['overtime']);
                    $appInfo['total_min'] = abs(($att['compensatory_consume'] + $min) - $att['overtime']);
                    $this->set_compensatory_consume($appInfo);
                    return true;
                }elseif(($att['compensatory_consume'] + $min) == $att['overtime']){
                    $compensatory_consume = $att['overtime'];
                }else{
                    $compensatory_consume = $att['compensatory_consume']+$min;
                }
                $update = $this->where($where)->setField('compensatory_consume',$compensatory_consume);
                
            }
        }else{
            $update = $this->where($where)->setField('compensatory_consume',$att['overtime']);
            //调休时间溢出，需要换行再写入剩余数
            $appInfo['total_min'] = abs($att['overtime'] - $min);
            $this->set_compensatory_consume($appInfo);
        }
        return true;
    }
    
    
    /**
     * 获取月结的缺勤分钟数
     * 月结缺勤数：月应出勤时数-月所有全薪假时数（不包含事假、半薪病假和标准薪病假）-月实际出勤时数（值为负数则不显示）
     * @param unknown $start
     * @param unknown $end
     * date:2017年3月31日
     * author: EK_熊
     */
    public function get_absent_mon($sn,$startDay,$endDay){
        $total_work = work_days_in_interval($startDay,$endDay,$sn);//时间范围：本月开始至昨天
        $sql_leave_type = sql_leave_type();
        $feild = "sum(work_actual) as work_actual,$sql_leave_type";
        $attday_mon = $this->where(['sn'=>$sn,'date'=>['between',["$startDay 00:00:00","$endDay 23:59:59"]]])->field($feild)->find();
        $absent = $total_work['min']- $attday_mon['work_actual'] -$attday_mon['total_leave'];

        return $absent;
    }
    
    
    /**
     * 外出申请的时间段和打卡时间重叠的情况
     * 计算外出申请的偏移量，修正 实际工时，有效工时，超时工时
     * @param array $app_map    需要获取申请数据的条件
     * date:2017年5月31日
     * author: EK_熊
     */
    public function overlap($app_map=[]){
        
        $applyData = M('Apply')->where($app_map)->field('sn,start_time,end_time,id,type,created_time')->select();
        $attDayModel = $this;
        $approverModel = D('Approver');
        $overlap = [];
        foreach ($applyData as $k =>$v){
            
            
            $diff = ceil((strtotime($v['end_time']) - strtotime($v['start_time']))/86400);
            for($i=0;$i<$diff;$i++){
                $intrval_sec = 0;
                $day = date('Y-m-d',strtotime($v['start_time'])+86400*$i);
                $apptime = $approverModel->get_time_start_end($v,$day);
                $a1 = strtotime($apptime['on']);
                $a2 = strtotime($apptime['off']);
                $map_att['date']=$day;
                $map_att['sn']=$v['sn'];
                $map_att['duty_on|duty_off']=['neq',''];
                $map_att['_string']='duty_on <> duty_off';
                $attInfo = $attDayModel->where($map_att)->field('duty_on as `on`,duty_off as `off`')->find();
                if (!$attInfo) continue;
                $b1 = strtotime($attInfo['on']);
                $b2 = strtotime($attInfo['off']);
                if ($b1 >= $a1 && $b2 >= $a2 && $a2 > $b1) {
                    //出现重叠，计算重叠分钟数
                    $intrval_sec = $a2-$b1;
                }
        
                if ($a1 >= $b1 && $a2 >= $b2 && $b2>$a1){
                    $intrval_sec = $b2-$a1;
                }
                if ($a1 > $b1 && $b2 > $a2 && $b2>$a1){
                    $intrval_sec = $a2-$a1;
                }               
                if ($a2 > $b2 && $a2 > $b1 && $b1>$a1){
                    $intrval_sec = $a2-$a1;
                }               
                
                $intrval_min = round($intrval_sec/60);
                if ($intrval_min >= 10) { //允许15分钟的重叠误差
//                     dump($day);
//                     dump($intrval_min);
//                     dump($v+$duty);
                    //填充偏移量到attendanceDay表
//                    $save['work_actual']=$attInfo['work_actual']-$intrval_min;
//                    $save['work_effect']=$attInfo['work_effect']-$intrval_min;
//                    $overtime = $save['work_effect']-480;
//                    $save['overtime']=$overtime > 0 ? $overtime : 0;
//                   $ret = $this->where(['date'=>$day,'sn'=>$v['sn']])->save($save);
                     $overlap[$day] = $intrval_min;
                }
            }
        }
        return $overlap;
    }

    
    //获取旷工次数,无记录的情况
    //获取旷工时长
    public function _absenteeism_num_no_data($sn,$start,$end){
        $abs_num = 0;
        $abs_num_total = 0; //一整天无记录
        $abs_min = 0; //缺勤时长，这里是记录缺时》=3小时的缺时时长
    
        $where['date'] = ['between',[$start,$end]];
        $where['sn'] = $sn;
        $leave_type = C('VACATION_TYPE_EN');
        $leave_type[] = 'leave_evection';
        $leave_type[] = 'leave_long_distance';
        
        $accountService = new AccountService();
        $userinfo = $accountService->get_user_by_field(['sn'=>$sn],'join_date,quit_date');
        $start_stmp = strtotime($start);
        $end_stmp = strtotime($end);
        
        foreach ($leave_type as $type){
            $sql_leave_type .= ", $type";
        }
        $attInfo= $this->where($where)->field('date,stu_absent,work_effect_918'.$sql_leave_type)->select();
        foreach ($attInfo as $k=>$v) {
            if ($v['date'] < $userinfo['join_date']) {
                continue;
            }
            $work_date[] = $v['date'];
            $attInfo[$k]['total_leav'] = 0;
            foreach ($v as $jkey=>$jval) {
                if (in_array_case($jkey, $leave_type)){
                    $attInfo[$k]['total_leave'] += $jval;
                }
            }
            if ((intval($v['work_effect_918'] + $attInfo[$k]['total_leave']) <= 300) && is_work_day($v['date'])){
                $abs_num++;
                $abs_min += 480-$v['work_effect_918']-$attInfo[$k]['total_leave'];
        
            }
        }



        //入职时间
        if (($userinfo['join_date'] >=$start) &&  ($userinfo['join_date'] <= $end)) {
            $start_stmp = strtotime($userinfo['join_date']);
        }
        //离职时间
        if (($userinfo['quit_date'] >=$start) &&  ($userinfo['quit_date'] <= $end)) {
            $end_stmp = strtotime($userinfo['quit_date']);
        }
        for($day=$start_stmp;$day<=$end_stmp;$day=$day+86400){
            $day_str = date('Y-m-d',$day);
            if (!in_array_case($day_str, $work_date) && is_work_day($day_str)){
                $abs_num_total++;
            }
        }

        $ret['num'] = $abs_num_total+$abs_num;
        $ret['min'] = $abs_num_total*480 + $abs_min;
        return $ret;
    }
    
    
}